#!/bin/sh
# vim: set ts=4:
#
# This jruby start script is rewrite of (poor) upstream's jruby.bash.
# It's POSIX compatible, more clean, with sensible defaults for Alpine Linux
# and without Windows and OS X stuff. Beside that it should support
# all the options of jruby.bash (hopefully) with the same behaviour.
#
# -----BEGIN HELP-----
#
# Environment variables:
#     CLASSPATH      Additional paths to be added to the end of the Java classpath.
#                    This may be overwritten by CLI argument -J-classpath or -J-cp.
#     JAVA_HOME      Path to directory where is JRE/JVM installed to use specific java/jdb binary,
#                    not the one on the PATH.
#     JAVA_OPTS      Default options to be passed to JVM.
#     JRUBY_HOME     Path to directory where JRuby is installed (default is /usr/share/java/jruby).
#     JRUBY_OPTS     Default JRuby command line arguments.
#     JRUBY_SHELL    Path to the system shell executable (default is /bin/sh).
#     PROFILE_ARGS   Arguments for the instrumented profiler.
#
# Deprecated environment variables (for compatibility):
#     JAVA_MEM       The -Xmx (maximum memory allocation) parameter for JVM (e.g. -Xmx128M).
#                    This may be overwritten by CLI argument -J-Xmx.
#     JAVA_STACK     The -Xss (stack size) parameter for JVM (default is -Xss2048k).
#                    This may be overwritten by CLI argument -J-Xss.
#     JAVA_VM        What JIT mode to use; -client, or -server (default is -server).
#                    This may be overwritten by CLI arguments --server, --client, and --dev.
#     JRUBY_PARENT_CLASSPATH  Classpath propagated from the parent JRuby process.
#
# All the environment variables are optional.
# -----END HELP-----
#
set -eu

readonly MAIN_CLASS_JRUBY='org.jruby.Main'
readonly MAIN_CLASS_NGCLIENT='org.jruby.util.NailMain'
readonly MAIN_CLASS_NGSERVER='org.jruby.main.NailServerMain'

readonly JRUBY_HOME="${JRUBY_HOME:-"/usr/share/jruby"}"
readonly JRUBY_CP="$JRUBY_HOME/lib/jruby.jar"
readonly JRUBY_SHELL="${JRUBY_SHELL:-"/bin/sh"}"
readonly NAILGUN_CMD="$JRUBY_HOME/tool/nailgun/ng"
readonly PROFILE_ARGS="${PROFILE_ARGS:-}"

quote() {
	local val; for val in "$@"; do
		printf %s "$val" | sed "s/'/'\\\\''/g; 1s/^/'/; \$s/\$/' /"
	done
}


java_stack="${JAVA_STACK:-"-Xss2048k"}"
java_cmd="${JAVA_HOME:+"$JAVA_HOME/bin/"}java"

java_opts="${JAVA_OPTS:-} ${JAVA_MEM:-} $java_stack
	-Dorg.xerial.snappy.use.systemlib=true"

# Use the same classpath propagated from parent jruby, if provided.
classpath="${JRUBY_PARENT_CLASSPATH:-}"
if [ -z "$classpath" ]; then
	_excluded='jruby.jar jruby-truffle.jar jruby-complete.jar'

	# Add all JARs from $JRUBY_HOME/lib to the classpath, except $_excluded.
	classpath=$(find "$JRUBY_HOME/lib" -maxdepth 1 \
		-name '*.jar' $(printf '! -name %s ' $_excluded) -print0 \
		| xargs -0 printf '%s:')
	classpath="${classpath%?}"  # %? removes leading ":"
fi

java_security_egd=''
if [ -r /dev/urandom ]; then
	# Non-file URL causes fallback to slow threaded SeedGenerator.
	# See https://bz.apache.org/bugzilla/show_bug.cgi?id=56139
	java_security_egd='file:/dev/urandom'
fi

extra_cp="${CLASSPATH:-}"
java_vm="${JAVA_VM:-"-server"}"
main_class="$MAIN_CLASS_JRUBY"
nailgun_client='no'
ruby_args=''
verify_jruby='no'


# Split out any -J argument for passing to the JVM.
# Scanning for args is aborted by '--'.
set -- ${JRUBY_OPTS:-} "$@"
while [ $# -gt 0 ]; do
	case "$1" in
	# Stuff after "-J" goes to JVM.
	-J | -J-X)
		"$java_cmd" -help
		printf '\n%s\n' '(Prepend -J in front of these options when using "jruby" command)'
		exit 1
	;;
	-J-classpath | -J-cp)
		extra_cp="$2"
		shift
	;;
	-J-ea)
		verify_jruby='yes'
		java_opts="$java_opts ${1#-J}"
	;;
	-J-Djava.security.egd=)
		java_security_egd="${1#-J-Djava.security.egd=}"
	;;
	-J*)
		java_opts="$java_opts ${1#-J}"
	;;
	 # Pass -X... and -X? search options through.
	 -X*\.\.\.|-X*\?)
		ruby_args="$ruby_args $1"
	;;
	 # Match -Xa.b.c=d to translate to -Da.b.c=d as a Java option.
	 -X*)
		val="${1#-X}"
		if expr "$val" : '.*[.]' > /dev/null; then
			java_opts="$java_opts -Djruby.$val"
		else
			ruby_args="$ruby_args -X$val"
		fi
	;;
	 # Match switches that take an argument.
	 -C | -e | -I | -S)
		ruby_args="$ruby_args $1 $(quote "$2")"
		shift
	;;
	 # Match same switches with argument stuck together.
	 -e* | -I* | -S*)
		ruby_args="$ruby_args $(quote "$1")"
	;;
	 # Run with JMX management enabled.
	 --manage)
		java_opts="$java_opts -Dcom.sun.management.jmxremote \
			-Djruby.management.enabled=true"
	;;
	 # Don't launch a GUI window, no matter what.
	 --headless)
		java_opts="$java_opts -Djava.awt.headless=true"
	;;
	 # Run under JDB (debug mode).
	 --jdb)
		java_cmd="${JAVA_HOME:+"$JAVA_HOME/bin/"}jdb"
		java_opts="$java_opts -sourcepath $JRUBY_HOME/lib/ruby/1.9:."
		ruby_args="$ruby_args -X+C"
	;;
	 --client | --server)
		java_vm="${1#?}"  # #? removes leading "-"
	;;
	 --dev)
		java_vm='-client'
		java_opts="$java_opts -XX:+TieredCompilation -XX:TieredStopAtLevel=1 \
			-Djruby.compile.mode=OFF -Djruby.compile.invokedynamic=false"
	;;
	 --sample)
		java_opts="$java_opts -Xprof"
	;;
	# Start up as Nailgun server.
	 --ng-server)
		main_class="$MAIN_CLASS_NGSERVER"
		verify_jruby='yes'
	;;
	# Use native Nailgun client to toss commands to server.
	 --ng-client | --ng)
		nailgun_client='yes'
	;;
	 # Warn but ignore.
	 --1.8 | --1.9 | --2.0)
		echo "WARNING: $1 is ignored"
	;;
	-h | -help | --help)
		"$java_cmd" -help
		# Print help from the top of this script.
		sed -En '/-{5}BEGIN HELP-{5}/,/-{5}END HELP-{5}/p' "$0" \
			| sed -E "s/^# ?//; 1d;\$d;"
		exit 0
	;;
	 # Abort processing on the double dash.
	 --)
		break
	;;
	 # Other opts go to ruby.
	 -*)
		ruby_args="$ruby_args $(quote "$1")"
	;;
	 # Abort processing on first non-opt arg.
	 *)
		break
	;;
	esac
	shift
done

# Remove possible duplications of some common JVM options.
for opt in -Xmx -Xms -Xss; do
	val=$(expr "$java_opts" : ".*$opt\([^ \t]*\).*" ||:)  # gets the later one
	if [ -n "$val" ]; then
		# Remove all occurrences of $opt and append the last one to the end.
		java_opts=$(printf "%s\n" "$java_opts" | sed "s/$opt[^ \t]*//g")
		java_opts="$java_opts ${opt}${val}"
	fi
done

classpath="$classpath${extra_cp:+":$extra_cp"}"
java_opts="$java_opts $java_vm
	${java_security_egd:+"-Djava.security.egd=$java_security_egd"}
	-Djruby.home=$JRUBY_HOME
	-Djruby.lib=$JRUBY_HOME/lib
	-Djruby.script=jruby
	-Djruby.shell=$JRUBY_SHELL"

# Append the rest of the arguments.
ruby_args="$ruby_args $(quote "$@")"

# Put $ruby_args back into the position arguments $1, $2, ...
# We must use eval to unquote arguments that we have quoted in order to
# preserve whitespaces.
eval "set -- $ruby_args"


if [ "$nailgun_client" = 'yes' ]; then
	if [ ! -f "$NAILGUN_CMD" ]; then
		echo 'ERROR: ng executable not found' 1>&2
		exit 1
	fi
	exec "$NAILGUN_CMD" $MAIN_CLASS_NGCLIENT "$@"

elif [ "$verify_jruby" = 'yes' ]; then
	[ -n "$PROFILE_ARGS" ] && echo 'Running with instrumented profiler'

	set +e
	"$java_cmd" $PROFILE_ARGS $java_opts \
		-classpath "$JRUBY_CP:$classpath" $main_class "$@"
	exit_code=$?
	set -e

	if [ -n "$PROFILE_ARGS" ]; then
		echo 'Profiling results:'
		cat profile.txt
		rm profile.txt
	fi

	exit $exit_code

else
	exec "$java_cmd" $java_opts -Xbootclasspath/a:"$JRUBY_CP" \
		${classpath:+"-classpath $classpath"} \
		$main_class "$@"
fi
